/**

common test class to use for testing WebSQL API implementation of nonWebSQL drivers

	crcDB - global variable defined in app/SQL.js
	initDB - function defined in app/SQL.js; simulated in the test case below

	tests below will involve SQL statements (both DML and DDL); however, its only purpose is
	to ensure that the existing WebSQL API is followed should those operations be successful. 
	The accuracy and correctness (e.g., that an error should occur if we attempt to DROP a non existing table)
	aimed at is only incidental.

*/

WebSQLAPICommonTest = Class.extend({

	localStorageDriver : "WebSQL"

	//simulation of initDB found in app/SQL.js
	//necessary to do this since we will be adding the logic here as to which local storage "driver" to use
    ,initDB : function(){
		
		
		var shortName = 'crcInv1';
		var version = '1.0';
		var displayName = 'CRC Database';
		var maxSize = 1024 * 1024 * 5; // in bytes		
		
		if( crcDB !== null ) return;
		
		if( this.localStorageDriver != WebSQLAPICommonTest.WEB_SQL ){
		
			crcDB = openDatabase2({
				shortName : shortName
			   ,dbName : shortName
			   ,version : version
			   ,displayName : displayName
			   ,maxSize  : maxSize
			   ,storageDriver : this.localStorageDriver
			   ,mode : 'sync' //set in synchronous mode for now since test classes is assuming sync. mode. but only localStorage driver will recognise this.
			});
			
			return;
		}
		
		try {

			if (!window.openDatabase) {
				alert('not supported');
			} else {
				var myDB = openDatabase(shortName, version, displayName, maxSize);
				//alert("database created");
			}
		} catch (e) {
			// Error handling code goes here.
			if (e == INVALID_STATE_ERR) {
				// Version number mismatch.
				alert("Invalid database version.");
			} else {
				alert("Unknown error " + e + ".");
			}
			return;
		}
		
		crcDB = myDB;
	
	}
	
	//this function is similar to the ExecuteReader* functions in the app.
	,executeReader : function(query, db, callback, _args){
			
		_args = _args || [];
		
		db.transaction(function (transaction) {
			transaction.executeSql(query, _args, callback, function (transaction, error) {
				callback(transaction, error);
			});
		});
		
	}
	
	//test parameterized queries.
	,test1 : function(){		
		
		var query = "select * from a";
		
		this.initDB();
		
		this.executeReader(query, crcDB, function (t, r_or_e) {				
			console.log(r_or_e);
		});
		
	}
	
	,_test_Drop_Table : function(){
	
		expectAsserts(2);
		
		var query = "CREATE TABLE non_existing (field1 INT)";
		
		this.initDB();
		
		this.executeReader(query, crcDB,
			function (t, r_or_e) {

				query = "DROP TABLE non_existing";
				ExecuteReaderWithSingleCallback(query, crcDB, 
					function(_t, _r_or_e){						
						assertTrue(
								 _r_or_e instanceof HPI.SQLResultSet
							&& _r_or_e.rowsAffected == 0
							&& _r_or_e.rows instanceof HPI.SQLResultSetRowList
							&& _r_or_e.rows.length == 0
						);
					}
				);
				
			}
		);
		
		//try dropping it again, and it should cause an error, since it was dropped in the previous code
		query = "DROP TABLE non_existing";
		
		ExecuteReaderWithSingleCallback(query, crcDB,
			function (t, r_or_e) {
				
				assertTrue(
						 r_or_e instanceof HPI.SQLError
					&& r_or_e.message !== undefined
					&& r_or_e.code == HPI.SQLError.SYNTAX_ERR
				);
				
			}
		);
		
	}

	//test both existing and non-existing
	,_test_Drop_Table_If_Exists : function(){
	
		expectAsserts(5);
		
		var query = "CREATE TABLE _existing (field1 INT)";
		
		this.initDB();
		
		this.executeReader(query, crcDB,
			function (t, r_or_e) {
			
				query = "DROP TABLE IF EXISTS _existing";
				ExecuteReaderWithSingleCallback(query, crcDB, 
					function(_t, _r_or_e){						
						assertTrue(
								 _r_or_e instanceof HPI.SQLResultSet
							&& _r_or_e.rowsAffected == 0
							&& _r_or_e.rows instanceof HPI.SQLResultSetRowList
							&& _r_or_e.rows.length == 0
						);
					}
				);
				
			}
		);

		//try deleting a really non-existing table, and it should not cause an SQLError
		query = "DROP TABLE IF EXISTS non_existing";
		
		this.executeReader(query, crcDB,
			function (t, r_or_e) {
				
				assertTrue(
						 r_or_e instanceof HPI.SQLResultSet
					&& r_or_e.rowsAffected == 0
					&& r_or_e.rows instanceof HPI.SQLResultSetRowList
					&& r_or_e.rows.length == 0
				);
				
			}
		);
		
		//now, "drop if it exists" the tables we will be using in the subsequent tests
		var tables = [
			"drop table if exists tbl1;"
		   ,"drop table if exists tbl2;"
		   ,"drop table if exists tbl3;"
		];
	
		this.initDB();
		
		HPI.each(tables, function(idx, item, items){
			this.executeReader(item, crcDB,
				function (t, r_or_e) {
					
					assertTrue(
							 r_or_e instanceof HPI.SQLResultSet
						&& r_or_e.rowsAffected == 0
						&& r_or_e.rows.length == 0
					);
				}
			);
		}, this);
		
	}

	,_test_Create_Table : function(){

		var tables = [
			"create table tbl1 (field1 integer, field2 int, field3 text);"
		   ,"create table tbl2 (field1 integer, field2 int, field3 text);"
		   ,"create table tbl3 (field1 integer, field4 text);"
		];
		
		expectAsserts(tables.length * 2);
	
		this.initDB();

		HPI.each(tables, function(idx, item, items){

			this.executeReader(item, crcDB,
				function (t, r_or_e) {

					assertTrue(
							 r_or_e instanceof HPI.SQLResultSet
						&& r_or_e.rowsAffected == 0
						&& r_or_e.rows instanceof HPI.SQLResultSetRowList
						&& r_or_e.rows.length == 0
					);
					
					//execute again, and it should result to an error since the table was already created.
					ExecuteReaderWithSingleCallback(item, crcDB,
						function(t, _r_or_e){
							assertTrue(
									 _r_or_e instanceof HPI.SQLError
								&& _r_or_e.message !== undefined
								&& _r_or_e.code == HPI.SQLError.SYNTAX_ERR
							);							
						}
					);					

				}
			);
			
		}, this);
		
	}
	
	,_test_Insert : function(){
		
		expectAsserts(5);
		
		//successful
		var query = [
			"INSERT INTO tbl1 VALUES(1,1,'a')"
		   ,"INSERT INTO tbl2 (field1, field2, field3) VALUES(1,2,'a')"
		   ,'INSERT INTO tbl2 (field1, field2, field3) VALUES(1,5,"a\'a")'
		   ,'INSERT INTO tbl3 (field1, field4) VALUES(1, "test")'
		];
		
		this.initDB();
		
		HPI.each(query, function(idx, item, items){
			this.executeReader(item, crcDB,
				function (t, r_or_e) {
					assertTrue(
							 r_or_e instanceof HPI.SQLResultSet
						&& r_or_e.rowsAffected >= 1
						&& r_or_e.insertId >= 1
						&& r_or_e.rows instanceof HPI.SQLResultSetRowList
						&& r_or_e.rows.length == 0
					);
				}
			);		
		}, this);
		
		//error
		var query = [
			"INSERT INTO non_existing_table(field1, field2, field3) VALUES(1,1,'a')"
		];

		HPI.each(query, function(idx, item, items){
			this.executeReader(item, crcDB,
				function (t, r_or_e) {
					assertTrue(
							 r_or_e instanceof HPI.SQLError
						&& r_or_e.message !== undefined
						&& r_or_e.code == HPI.SQLError.SYNTAX_ERR
					);
				}
			);		
		}, this);
		
	}

	,_valid_Error_Result : function(e){
		
		return (
				e instanceof HPI.SQLError
			&& e.message !== undefined
			&& typeof e.message == 'string'
			
			&& e.code !== undefined
			&& typeof e.code == 'number'
		);
		
	}
	
	,_tbl1_Select_Success_Result : function(r){

		return (
		
				 r instanceof HPI.SQLResultSet
				 
			&& r.insertId === undefined 
						 
			&& r.rowsAffected !== undefined				
			&& typeof r.rowsAffected == 'number'
			&& r.rowsAffected == 1
			
			&& r.rows instanceof HPI.SQLResultSetRowList
			&& r.rows.length !== undefined
			&& typeof r.rows.length == 'number'
			&& r.rows.length == 1

			&& r.rows.item !== undefined
			&& typeof r.rows.item == 'function'
			
			&& r.rows.item(0) !== undefined
			&& typeof r.rows.item(0) == 'object'
			&& r.rows.item(0).field1 === 1
		);
		
	}
	
	,_tbl2_Select_Success_Result : function(r){
	
		return (
		
				 r instanceof HPI.SQLResultSet
				 
			&& r.insertId === undefined 
				 
			&& r.rowsAffected !== undefined				
			&& typeof r.rowsAffected == 'number'
			&& r.rowsAffected == 1
			
			&& r.rows instanceof HPI.SQLResultSetRowList
			&& r.rows.length !== undefined
			&& typeof r.rows.length == 'number'
			&& r.rows.length == 2

			&& r.rows.item !== undefined
			&& typeof r.rows.item == 'function'
			
			&& r.rows.item(0) !== undefined
			&& typeof r.rows.item(1) == 'object'
			&& r.rows.item(1).field2 === 5
			
		);
		
	}
	
	//@todo: test with field specification
	,_test_Select : function(){
	
		this.initDB();
		
		this.executeReader("SELECT * FROM tbl1", crcDB, function(trxn, r_or_e){
			assertTrue((new WebSQLAPICommonTest())._tbl1_Select_Success_Result(r_or_e));			
		});		
		
		this.executeReader("SELECT field1, field2,field3 FROM tbl1", crcDB, function(trxn, r_or_e){

			assertTrue(
						(new WebSQLAPICommonTest())._tbl1_Select_Success_Result(r_or_e)
				//&& 	r_or_e.rows.item(0).field3 === undefined	
			);
		});
		
		this.executeReader("SELECT * FROM tbl1 WHERE field1 = ?", crcDB, function(trxn, r_or_e){
			assertTrue((new WebSQLAPICommonTest())._tbl1_Select_Success_Result(r_or_e));
		}, [1]);
		
		this.executeReader("SELECT * FROM tbl2", crcDB, function(trxn, r_or_e){
			assertTrue((new WebSQLAPICommonTest())._tbl2_Select_Success_Result(r_or_e));
		});
		
	}
	
	,_test_Select_Error : function(){
	
		var queries = [
			"SELECT * FROM non_existing_tbl"
		   ,"SELECT * INVALID_SYNTAX tbl1"
		];

		this.initDB();
		
		HPI.each(queries, function(idx, item, items){
			this.executeReader(item, crcDB, function(trxn, r_or_e){
				assertTrue((new WebSQLAPICommonTest())._valid_Error_Result(r_or_e));
			});
		}, this);
		
	}
	
	//tests that actually use the ExecuteReader* functions used heavily in the app.
    ,_test_ExecuteReaderWithSingleCallback_1 : function(){

		expectAsserts(2);
		
		this.initDB();
		
		ExecuteReaderWithSingleCallback("SELECT * FROM tbl1", crcDB, function(trxn, r_or_e){
			assertTrue((new WebSQLAPICommonTest())._tbl1_Select_Success_Result(r_or_e));
		});
		
		ExecuteReaderWithSingleCallback("SELECT * FROM tbl2", crcDB, function(trxn, r_or_e){
			assertTrue((new WebSQLAPICommonTest())._tbl2_Select_Success_Result(r_or_e));
		});
		
	}
	
    ,_test_ExecuteReaderWithSingleCallback_2 : function(){

		expectAsserts(2);

		var queries = [
			"SELECT * FROM non_existing_tbl"
		   ,"SELECT * INVALID_SYNTAX tbl1"
		];

		this.initDB();
		
		HPI.each(queries, function(idx, item, items){
			ExecuteReaderWithSingleCallback(item, crcDB, function(trxn, r_or_e){
				assertTrue((new WebSQLAPICommonTest())._valid_Error_Result(r_or_e));
			});
		}, this);
		
	}	
	
	,_test_ExecuteReader_1 : function(){

		expectAsserts(1);
		
		this.initDB();
		var query = "Select * From tbl1"		
		ExecuteReader(query, crcDB,
			//this is the callback that must be called
			function (t, r) {
				assertTrue((new WebSQLAPICommonTest())._tbl1_Select_Success_Result(r));				
			}
		   ,function(t, e){
				fail("Error callback must not be called.");
		   }
		);		
	}
	
	,_test_ExecuteReader_2 : function(){
	
		expectAsserts(2);
		
		var queries = [
			"SELECT * FROM non_existing_tbl"
		   ,"SELECT * INVALID_SYNTAX tbl1"
		];

		this.initDB();
		
		HPI.each(queries, function(idx, item, items){
			ExecuteReader(item, crcDB,
				function (t, r){
					fail("Error callback must not be called.");
				}
				//this is the callback that must be called
			   ,function(t, e){
					assertTrue((new WebSQLAPICommonTest())._valid_Error_Result(e));
			   }
			);
		}, this);
		
	}
	
	,_test_ExecuteQuery_1 : function(){
		
		this.initDB();
		var query = "SELECT * FROM tbl1"	
		ExecuteReader(query, crcDB);
		
	}

	,_test_ExecuteQuery_2 : function(){
	
		this.initDB();
		var query = "sSelect * from tbl1"	
		ExecuteReader(query, crcDB);
		
	}
	
	,_test_Update : function(){
		
		expectAsserts(3);
		
		this.initDB();
		var query = "UPDATE tbl1 SET field1 = 4";
		ExecuteReaderWithSingleCallback(query, crcDB, function(trxn, r_or_e){
			
			assertTrue(
					r_or_e instanceof HPI.SQLResultSet

				&& r_or_e.insertId === undefined 
							 
				&& r_or_e.rowsAffected !== undefined				
				&& typeof r_or_e.rowsAffected == 'number'
				&& r_or_e.rowsAffected == 1
				
				&& r_or_e.rows instanceof HPI.SQLResultSetRowList
				&& r_or_e.rows.length !== undefined
				&& typeof r_or_e.rows.length == 'number'
				&& r_or_e.rows.length == 0

				&& r_or_e.rows.item !== undefined
				&& typeof r_or_e.rows.item == 'function'
			);
			
			query = "SELECT * FROM tbl1;";
			ExecuteReaderWithSingleCallback(query, crcDB, function(t, _r_or_e){

				assertTrue(
						_r_or_e instanceof HPI.SQLResultSet

					&& _r_or_e.insertId === undefined 
								 
					&& _r_or_e.rowsAffected !== undefined				
					&& typeof _r_or_e.rowsAffected == 'number'
					&& _r_or_e.rowsAffected == 1
					
					&& _r_or_e.rows instanceof HPI.SQLResultSetRowList
					&& _r_or_e.rows.length !== undefined
					&& typeof _r_or_e.rows.length == 'number'
					&& _r_or_e.rows.length == 1

					&& _r_or_e.rows.item !== undefined
					&& typeof _r_or_e.rows.item == 'function'
					
					&& _r_or_e.rows.item(0) !== undefined
					&& typeof _r_or_e.rows.item(0) == 'object'
					&& _r_or_e.rows.item(0).field1 === 4
				);
			
			});
			
		});
		
		var query = "UPDATE non_existing SET field1 = 1";
		ExecuteReaderWithSingleCallback(query, crcDB, function(trxn, r_or_e){
			assertTrue((new WebSQLAPICommonTest())._valid_Error_Result(r_or_e));
		});
		
	}
	
	,_test_Delete : function(){
	
		expectAsserts(2);

		this.initDB();
		
		var query = "DELETE * FROM tbl2";
		ExecuteReaderWithSingleCallback(query, crcDB, function(trxn, r_or_e){
			
			assertTrue(
					 r_or_e instanceof HPI.SQLResultSet
					 
				&& r_or_e.insertId === undefined 
							 
				&& r_or_e.rowsAffected !== undefined				
				&& typeof r_or_e.rowsAffected == 'number'
				&& r_or_e.rowsAffected == 2
				
				&& r_or_e.rows instanceof HPI.SQLResultSetRowList
				&& r_or_e.rows.length !== undefined
				&& typeof r_or_e.rows.length == 'number'
				&& r_or_e.rows.length == 0

				&& r_or_e.rows.item !== undefined
				&& typeof r_or_e.rows.item == 'function'
			);
			
		});
		
		var query = "DELETE * FROM non_existing";
		ExecuteReaderWithSingleCallback(query, crcDB, function(trxn, r_or_e){
			assertTrue((new WebSQLAPICommonTest())._valid_Error_Result(r_or_e));
		});
		
	}

	//@todo: test with field specification but on a different casing
	,_test_Ensure_Table_Referencing_Is_Case_Insensitive : function(){

		expectAsserts(6);
		
		this.initDB();

		this.executeReader("UPDATE TbL1 SET field1 = 1", crcDB, function(trxn, r_or_e){
			assertTrue(r_or_e instanceof HPI.SQLResultSet);
		});
		
		this.executeReader("SELECT * FROM tbL1", crcDB, function(trxn, r_or_e){
			assertTrue((new WebSQLAPICommonTest())._tbl1_Select_Success_Result(r_or_e));
		});
		
		// this.executeReader("SELECT FIEld1, fieLD2 FROM tbL1", crcDB, function(trxn, r_or_e){
			// assertTrue(
						// (new WebSQLAPICommonTest())._tbl1_Select_Success_Result(r_or_e)
				// && 	r_or_e.rows.item(0).field3 === undefined
			// );
		// });
		
		this.executeReader("INSERT INTO tBL1 VALUES(5,1,'a')", crcDB, function(trxn, r_or_e){
			assertTrue(r_or_e instanceof HPI.SQLResultSet);
		});
		
		this.executeReader("DELETE * FROM tBL1", crcDB, function(trxn, r_or_e){
			assertTrue(
					r_or_e instanceof HPI.SQLResultSet
				&& r_or_e.rowsAffected == 2
			);
		});
		
		this.executeReader("DROP TABLE TBl1", crcDB, function(trxn, r_or_e){
			assertTrue(
					r_or_e instanceof HPI.SQLResultSet
			);
		});

		this.executeReader("DROP TABLE tBL2", crcDB, function(trxn, r_or_e){
			assertTrue(
					r_or_e instanceof HPI.SQLResultSet
			);
		});
		
	}

});

/*
{
	tables : {
		table1 : {
			rows : [
				{}
			]
			,fields : {
				field1 : {type: integer , isPrimaryKey : false, isNullable : true}
			}
		}
	   ,table2 : {
			rows : [
				{}
			]
			,fields : {
				field1 : {type: integer , isPrimaryKey : false, isNullable : true}
			}
	   }
	}
}
*/

WebSQLAPICommonTest.WEB_SQL = "WebSQL";
WebSQLAPICommonTest.INDEXED_DB = "IndexedDB";
WebSQLAPICommonTest.LOCAL_STORAGE = "LocalStorage";